package Server;

import OtherFuction.GetTime;
import OtherFuction.ToHex;
import ReadData.ReadData1997;
import ReadData.ReadData2007;
import constant.RedisCons;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.net.InetSocketAddress;
import java.util.Collection;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * netty ChannelInboundHandle
 * 
 * @author ack @date Oct 28, 2019
 *
 */
public class ServerHandler extends ChannelInboundHandlerAdapter {

	private static Logger log = LoggerFactory.getLogger(ServerHandler.class);

	private static int connectNum;

	private static int getConnectNum() {
		return ServerHandler.connectNum;
	}

	private static void setConnectNum(int connectNum) {
		ServerHandler.connectNum = connectNum;
	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		/** 建立连接后连接数+1 **/

		int connectNum = getConnectNum() + 1;
		setConnectNum(connectNum);

		InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
		String clientIP = insocket.getAddress().getHostAddress();
		Server.connectedEquId2Ctx.put(clientIP + ":" + insocket.getPort(), ctx);
		log.info("#新电表已连接，#clientCount:{} #equId2Ctx:[{}]", connectNum, Server.connectedEquId2Ctx);
	}

	/**
	 * 先解析数据，然后根据特征位判断返回的应答种类，之后进行相应的操作
	 **/
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		log.debug("#接收数据服务线程：[{}]", Thread.currentThread().getName());

		ByteBuf buf = (ByteBuf) msg;
		/** req里保存Byte数组 */
		byte[] req = new byte[buf.readableBytes()];
		buf.readBytes(req);
//		log.info("#接收桢数据:[{}]", ToHex.ToHex(req));
		// 解析前需要清理FE激活码
		byte[] newReq = new byte[req.length - 4];
		System.arraycopy(req, 4, newReq, 0, req.length - 4);
		req = newReq;
		log.debug("#接收数据-处理后:[{}]", ToHex.ToHex(req));
		// 判断是否电表首次响应，如果首次响应那么就获取、存储电表地址域
		InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
		String equId = insocket.getAddress().getHostAddress() + ":" + insocket.getPort();
		if (!App.initEquIdSet.contains(equId)) {
			byte[] address = new byte[6];
			System.arraycopy(req, 1, address, 0, 6);
//			 RedisPool.setByte((RedisCons.address + equId).getBytes(), address);
			App.runningEquIdAddressMap.put(equId, address);
			log.info("#电表初始化成功，#key:[{}],#地址域:[{}]", equId, ToHex.ToHex(address));
			App.initEquIdSet.add(equId);
			return;
		} else {
//			log.debug("#开始解析数据");
		}

		if (req.length > 8) {

			/** 返回的为2007电表的各项数据，进行解析并写入数据库 **/
			if (req[8] == (byte) 0x91 || req[8] == (byte) 0xb1) {

				double result = 0;
				ReadData2007 readData2007 = new ReadData2007();
				result = readData2007.Read_data(req, insocket.getAddress().getHostAddress(), insocket.getPort());
				readData2007 = null;
//				log.info("completed !!" + "\n");
			} else {
				log.error("#数据域解析异常");
			}
		}
		/** 2007从站异常应答 **/
		else if ((req[8] | 0xf0) == 0xd0) {
			// TODO 邮件告警
			log.info("Error2007 * 1 ");
		}

		/** 1997电表返回数据 **/
		else if (req[8] == (byte) 0x81) {
			String result = null;
			ReadData1997 readData1997 = new ReadData1997();
			result = readData1997.Read_data(req);
			readData1997 = null;
			switch (result) {
			case "bit":
				log.info("It is just bit!" + "\n");
				break;
			case "error":
				log.info(GetTime.NowTime() + " " + "异常应答：" + ToHex.ToHex(req) + "\n");
				break;
			case "checking_error":
				log.info(GetTime.NowTime() + " " + "验证异常：" + ToHex.ToHex(req) + "\n");
				break;
			default:
				log.info("completed !!" + "\n");
			}
			result = null;

		}

		buf.release();
		buf = null;

	}

	/** 连接中断，去除ctxMap中相应的ctx **/
	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		int connectNum = getConnectNum() - 1;
		setConnectNum(connectNum);

		if (connectNum == 0) {
			log.info("#All Connections Are Closed!!");
		}
		InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
		log.info("#电表断开连接：{}", insocket.getHostString() + ":" + insocket.getPort());
		Collection values = Server.connectedEquId2Ctx.values();
		values.remove(ctx);
		String equId = insocket.getAddress().getHostAddress() + ":" + insocket.getPort();
		App.runningEquIdAddressMap.remove(equId);
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable t) {
		InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
		log.info("#exceptionCaught Client :{}", insocket.getHostString() + ":" + insocket.getPort() + t.getMessage(),
				t);
	}

}
